﻿namespace Snipps.ViewModels.Pages
{
    using System.Collections.Generic;
    using System.Linq;
    using System.Windows.Input;

    using MvvmFoundation.Wpf;

    using Snipps.Core;
    using Snipps.Core.Entities;
    using Snipps.Infrastructure;
    using Snipps.Infrastructure.Services;
    using Snipps.Langs;
    using Snipps.Messages;
    using Snipps.Services;
    using Snipps.Validators;
    using Snipps.ViewModels.Filters;

    using Messenger = GalaSoft.MvvmLight.Messaging.Messenger;
    using RelayCommand = GalaSoft.MvvmLight.Command.RelayCommand;

    public class EditSnippetViewModel : ValidatableViewModelBase, IHasProgrammingLangsFilter, IHasTagsFilter, IPageViewModel
    {
        public const string SnippetNamePropName = "SnippetName";

        public const string SnippetNamePropertyName = "SnippetName";

        public const string SnippetContentPropertyName = "SnippetContent";

        public const string EditedSnippetPropertyName = "EditedSnippet";

        public const string IsCompletionListVisiblePropertyName = "IsCompletionListVisible";

        private readonly IList<IsActive> _actives;

        private readonly IList<PropertyObserver<IsActive>> _activesObservers;

        private readonly EditSnippetValidator _addNewSnippetValidator;

        private readonly ActiveViewModel _cancelButtonActive;

        private readonly IExportImportSnippetService _exportImportSnippetService;

        private readonly ActiveViewModel _saveButtonActive;

        private readonly ActiveViewModel _snippetContentActive;

        private readonly ActiveViewModel _snippetNameActive;

        private readonly ISnippetsDataContext _snippetsDataContext;

        private readonly ProgrammingLangFilterViewModel _syncLangFilter;

        private readonly TagFilterViewModel _tagFilter;

        private IFilterViewModel _currentFilter;

        private Snippet _editedSnippet;

        private bool _isActive;

        private bool _isCompletionListVisible;

        private IsActive _lastActive;

        private string _name;

        private string _snippetContent;

        private string _snippetName;

        public EditSnippetViewModel(
            ISnippetsDataContext snippetsDataContext, 
            IThreadingService threadingService, 
            IExportImportSnippetService exportImportSnippetService)
        {
            _snippetsDataContext = snippetsDataContext;
            _exportImportSnippetService = exportImportSnippetService;

            _actives = new List<IsActive>();

            _tagFilter = new TagFilterViewModel(snippetsDataContext, threadingService);
            _syncLangFilter = new ProgrammingLangFilterViewModel(snippetsDataContext, threadingService);
            _snippetNameActive = new ActiveViewModel();
            Validator = new EditSnippetValidator();
            _addNewSnippetValidator = new EditSnippetValidator(IsSnippetExist);

            _snippetContentActive = new ActiveViewModel();
            SnippetContent = "sample content";
            _saveButtonActive = new ActiveViewModel();
            _cancelButtonActive = new ActiveViewModel();

            _actives.Add(_snippetNameActive);
            _actives.Add(_syncLangFilter);
            _actives.Add(_tagFilter);
            _actives.Add(_snippetContentActive);
            _actives.Add(_saveButtonActive);
            _actives.Add(_cancelButtonActive);

            _activesObservers = new List<PropertyObserver<IsActive>>();

            foreach (BindableBase active in _actives)
            {
                if (active != null)
                {
                    var activeObserver = new PropertyObserver<IsActive>(active as IsActive);
                    activeObserver.RegisterHandler(x => x.IsActive, OnActiveChanged);
                    _activesObservers.Add(activeObserver);
                }
            }

            SaveCommand = new RelayCommand(OnSaveSnippet, () => IsValid);
            CancelCommand = new RelayCommand(OnCancel);
        }

        public ActiveViewModel SnippetNameActive
        {
            get
            {
                return _snippetNameActive;
            }
        }

        public string SnippetName
        {
            get
            {
                return _snippetName;
            }

            set
            {
                SetProperty(ref _snippetName, value, SnippetNamePropertyName);
            }
        }

        public Snippet EditedSnippet
        {
            get
            {
                return _editedSnippet;
            }

            set
            {
                SetProperty(ref _editedSnippet, value, EditedSnippetPropertyName, OnEditedSnippetChanged);
            }
        }

        public ActiveViewModel CancelButtonActive
        {
            get
            {
                return _cancelButtonActive;
            }
        }

        public IFilterViewModel CurrentFilter
        {
            get
            {
                return _currentFilter;
            }

            set
            {
                SetProperty(ref _currentFilter, value, "CurrentFilter");
            }
        }

        public bool IsCompletionListVisible
        {
            get
            {
                return _isCompletionListVisible;
            }

            set
            {
                SetProperty(ref _isCompletionListVisible, value, IsCompletionListVisiblePropertyName);
            }
        }

        public ActiveViewModel SaveButtonActive
        {
            get
            {
                return _saveButtonActive;
            }
        }

        public ICommand SaveCommand { get; private set; }

        public ICommand CancelCommand { get; private set; }

        public string SnippetContent
        {
            get
            {
                return _snippetContent;
            }

            set
            {
                SetProperty(ref _snippetContent, value, SnippetContentPropertyName);
            }
        }

        public ActiveViewModel SnippetContentActive
        {
            get
            {
                return _snippetContentActive;
            }
        }

        public ProgrammingLangFilterViewModel ProgrammingLangFilter
        {
            get
            {
                return _syncLangFilter;
            }
        }

        public TagFilterViewModel TagFilter
        {
            get
            {
                return _tagFilter;
            }
        }

        public string Name
        {
            get
            {
                return _name;
            }
        }

        public bool IsActive
        {
            get
            {
                return _isActive;
            }

            set
            {
                SetProperty(ref _isActive, value, "IsActive", OnIsActiveChanged);
            }
        }

        private void OnCancel()
        {
            Messenger.Default.Send(new CancelMsg());
        }

        public override void Cleanup()
        {
            base.Cleanup();

            _editedSnippet = null;
            _syncLangFilter.Cleanup();
            _tagFilter.Cleanup();
            RemoveAllErrors();
        }

        private void OnEditedSnippetChanged()
        {
            if (EditedSnippet == null)
            {
                return;
            }

            if (!EditedSnippet.IsNew)
            {
                _name = Lang.EditSnippetViewName;
            }
            else
            {
                _name = Lang.AddNewSnippetViewName;
            }

            var snippetToEdit = EditedSnippet;
            SnippetName = snippetToEdit.Name;
            _syncLangFilter.CurrentProgrammingLang = snippetToEdit.ProgrammingLang;
            if (!string.IsNullOrEmpty(snippetToEdit.Content))
            {
                SnippetContent = snippetToEdit.Content;
            }
            else
            {
                var importedSnippet = _exportImportSnippetService.Import();
                if (importedSnippet != null)
                {
                    SnippetContent = importedSnippet.Content;
                }
            }

            if (SnippetContent == null)
            {
                SnippetContent = string.Empty;
            }

            _tagFilter.FilterTags.Clear();
            foreach (var tag in snippetToEdit.Tags)
            {
                _tagFilter.FilterTags.Add(tag);
            }
        }

        private void OnIsActiveChanged()
        {
            if (IsActive)
            {
                SnippetContentActive.IsActive = true;
            }
            else
            {
                Cleanup();
            }
        }

        private void OnActiveChanged(IsActive sActive)
        {
            if (_lastActive != null)
            {
                _lastActive.IsActive = false;
            }

            var active = _actives.FirstOrDefault(x => x.IsActive);
            var activeFilter = active as IFilterViewModel;
            if (activeFilter != null)
            {
                IsCompletionListVisible = activeFilter.CompletionList != null;
            }
            else
            {
                if (active != null)
                {
                    IsCompletionListVisible = false;
                }
            }

            CurrentFilter = activeFilter;

            _lastActive = active;
        }

        private bool IsSnippetExist(string name, ProgrammingLang programmingLang)
        {
            var snippet = _snippetsDataContext.GetSnippet(name, programmingLang);

            if (snippet == null)
            {
                return false;
            }

            return !snippet.Equals(EditedSnippet);
        }

        private void OnSaveSnippet()
        {
            Validate(_addNewSnippetValidator);
            if (HasErrors)
            {
                return;
            }

            EditedSnippet.Name = _snippetName;
            EditedSnippet.ProgrammingLang = _syncLangFilter.CurrentProgrammingLang;
            EditedSnippet.Content = SnippetContent;
            EditedSnippet.Tags.Clear();
            foreach (var tag in _tagFilter.FilterTags)
            {
                EditedSnippet.Tags.Add(tag);
            }

            _snippetsDataContext.SaveSnippet(EditedSnippet);

            Messenger.Default.Send(new ItemChangedMsg { Item = EditedSnippet });
        }
    }
}